/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package protect02;

import java.io.*;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

import protect03.EuclidExtended;

/**
 *
 * @author Студент
 */
public class Protect02 {

    /**
     * @param args the command line arguments
     */
    
    public static void main(String[] args) throws IOException, FileNotFoundException, ClassNotFoundException {
        test02();
    }
    
    public static void test01() throws IOException {
        
        Random rnd = new Random();
        
        BigInteger base = BigInteger.valueOf(100000000000L);
        
        BigInteger p = base.add(new BigInteger(30, rnd)).nextProbablePrime();
        BigInteger q = base.add(new BigInteger(30, rnd)).nextProbablePrime();
        
        println(String.format("p = %s", p));
        println(String.format("q = %s", q));
        
        
        BigInteger n = p.multiply(q);
        byte[] nByte = n.toByteArray();
        
        BigInteger psqs = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
        
        EuclidExtended ep = EuclidExtended.generateDualPrimary(n.bitLength() - 1, psqs);
        BigInteger e = ep.b;
        BigInteger d = ep.y;
        
        println(String.format("MOD = %d", ep.d));
        println(String.format("n = %s", n));
        println(String.format("e = %s", e));
        println(String.format("d = %d", d));
        
        BigInteger bitest = d.multiply(e).mod(psqs);
        println(String.format("bitest = %s", bitest));
        
        //int k = (n.bitLength() + 1) / 8;
        int K = nByte.length;
        int k = nByte.length - 1; 
        int ks = k - 1;
        
        println(String.format("k = %d", k));
        {
            BigInteger M = BigInteger.valueOf(100500);

            BigInteger C = M.modPow(e, n);

            BigInteger MM = C.modPow(d, n);

            println(String.format("M = %d", M));
            println(String.format("C = %d", C));
            println(String.format("MM = %d", MM));
        }
        
        
        String msg = "Раз, два, три, четыре, пять\n"
                + "Вышел зайчик погулять.\n"
                + "Вдруг охотник выбегает,\n"
                + "Прямо в зайчика стреляет!\n"
                + "Пиф-паф, ой-ой-ой!\n"
                + "Помирает зайчик мой! One";
        
        //String msg = "ONe Two Three Four Five Раз два трич етыре пять";
        
        ArrayList<BigInteger> alb = new ArrayList<BigInteger>();
        
        byte[] buf = msg.getBytes();       
        
        //ByteArrayOutputStream 
        ByteArrayInputStream bais = new ByteArrayInputStream(buf);
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        
        byte[] zeroBuf = new byte[K];
        byte[] readBuf = new byte[k];
        readBuf[0] = 0;
        int readed = bais.read(readBuf, 1, readBuf.length - 1);
        while (readed >= readBuf.length - 1) {
     
            println(String.format("readed = %d length = %d", readed, readBuf.length));
            BigInteger parted = new BigInteger(readBuf);
            println(String.format("parted = %s size = %d", parted, parted.toByteArray().length));
            
            BigInteger coded = parted.modPow(e, n);
            println(String.format("coded = %s size = %d", coded, coded.toByteArray().length));
            alb.add(coded);
            
            byte[] codeda = coded.toByteArray();
            /*
            byte[] temp = new byte[K];
            int from = temp.length - codeda.length;
            int to = codeda.length;
            System.arraycopy(codeda, 0, temp, from, to);
            baos.write(temp);
            */
            
            int zeros = K - codeda.length;
            baos.write(zeroBuf, 0, zeros);
            println(String.format("writed zeros = %d", zeros));
            baos.write(codeda);
            println(String.format("writed codeda.length = %d", codeda.length));
            
            readed = bais.read(readBuf, 1, readBuf.length - 1);
        }
        if (readed > 0 /*&& readed < readBuf.length - 1*/) {
            byte dummy = 0;
            Arrays.fill(readBuf, readed + 1, readBuf.length, dummy);
            println(String.format("filled %d by %d", readBuf.length - readed, dummy));
            
            println(String.format("readed = %d length = %d", readed, readBuf.length));
            BigInteger parted = new BigInteger(readBuf);
            println(String.format("parted = %s size = %d", parted, parted.toByteArray().length));
            
            BigInteger coded = parted.modPow(e, n);
            println(String.format("coded = %s size = %d", coded, coded.toByteArray().length));
            alb.add(coded);
            
            byte[] codeda = coded.toByteArray();
            /*
            byte[] temp = new byte[K];
            int from = temp.length - codeda.length;
            int to = codeda.length;
            System.arraycopy(codeda, 0, temp, from, to);
            baos.write(temp);
            */
            
            int zeros = K - codeda.length;
            baos.write(zeroBuf, 0, zeros);
            println(String.format("writed zeros = %d", zeros));
            baos.write(codeda);
            println(String.format("writed codeda.length = %d", codeda.length));
        }
        {
            DataOutputStream dos = new DataOutputStream(baos);
            dos.writeInt(readed);
            println(String.format("written = %d, %d", readed, dos.size()));
        }
        
        //byte[] buf = Base64.encode(msg.getBytes()).getBytes(); 
        /*
        int delta = buf.length % k;
        println(String.format("buf.length = %d", buf.length));
        println(String.format("delta = %d", delta));
        if (delta != 0) {
            byte[] buf2 = new byte[buf.length + k - delta];
            for (int ib = 0; ib < buf.length; ib++) {
                buf2[ib] = buf[ib];
            }
            
            for (int ib = buf.length; ib < buf2.length; ib++) {
                buf2[ib] = 0;
            }
            buf = buf2;
        }
        
        
        println(String.format("buf.length = %d", buf.length));
        String test = new String(buf).trim();
        println(String.format("test = %s", test));
        
        int parts = buf.length / k;
        println(String.format("parts = %d", parts));
        
        println("\n\nENCODING");
        
        for (int ip = 0; ip < buf.length; ip += k) {
            byte[] ci = Arrays.copyOfRange(buf, ip, ip + k);
            
            println(String.format("C%d length = %d", ip, ci.length));
            BigInteger parted = new BigInteger(ci);
            println(String.format("parted = %s size = %d", parted, parted.toByteArray().length));
            
            BigInteger coded = parted.modPow(e, n);
            println(String.format("coded = %s size = %d", coded, coded.toByteArray().length));
            alb.add(coded);
        }
        * */
        
        println("\n\nDECODING");
        
        ByteArrayInputStream bis = new ByteArrayInputStream(baos.toByteArray());
        baos.reset();
        
        byte[] temp = new byte[K];
        bis.read(temp);
        
        byte[] temp1 = new byte[K];
        readed = bis.read(temp1);
        
        while (readed == K) {
            //byte[] temp = new byte[K];
            //bis.read(temp);
            
            BigInteger encoded = new BigInteger(temp);
            byte[] encodeda = encoded.toByteArray();
            println(String.format("encoded = %s length = %d", encoded, encodeda.length));
            
            BigInteger decoded = encoded.modPow(d, n);
            byte[] decodeda = decoded.toByteArray();
            println(String.format("decoded = %s length = %d", decoded, decodeda.length));
            
            
            byte[] orig = new byte[ks];
            System.arraycopy(decodeda, decodeda.length - ks, orig, 0, orig.length);
            baos.write(orig);
            
            
            //int zeros = ks - decodeda.length;
            //baos.write(zeroBuf, 0, zeros);
            //println(String.format("writed zeros = %d", zeros));
            //baos.write(decodeda);
            //println(String.format("writed decodeda = %d", decodeda.length));
            
            byte[] tt = temp1;
            temp1 = temp;
            temp = tt;
                      
            readed = bis.read(temp1);
        }
        if (readed > 0) {
            DataInputStream dis = new DataInputStream(new ByteArrayInputStream(temp1));
            readed =  dis.readInt();
            println(String.format("left = %d", readed));
            
            BigInteger encoded = new BigInteger(temp);
            byte[] encodeda = encoded.toByteArray();
            println(String.format("encoded = %s length = %d", encoded, encodeda.length));
            
            BigInteger decoded = encoded.modPow(d, n);
            byte[] decodeda = decoded.toByteArray();
            println(String.format("decoded = %s length = %d", decoded, decodeda.length));
            
            
            byte[] orig = new byte[ks];
            System.arraycopy(decodeda, decodeda.length - ks, orig, 0, orig.length);
            baos.write(orig, 0, readed);
        }
        
        String s = new String(baos.toByteArray());
        /*
        if (readed > 0) {
            byte[] from = baos.toByteArray();
            byte[] to = new byte[from.length - ks + readed];
            System.arraycopy(from, 0, to, 0, to.length);
            s = new String(to);
        }
        */
        println(s);
        
        /*
        baos.reset();
        for (BigInteger coded : alb) {
            println(String.format("encoded = %s length = %d", coded, coded.toByteArray().length));
            BigInteger decoded = coded.modPow(d, n);
            println(String.format("decoded = %s length = %d", decoded, decoded.toByteArray().length));
            byte[] mi = decoded.toByteArray();
            for (int i = 0; i < mi.length; i++) {
                //buf3[ib + i] = mi[i];
            }
            baos.write(mi);
        }
        
        String decoded2 = new String(baos.toByteArray()).trim();
        //String decoded2 = new String(Base64.decode((new String(baos.toByteArray())).trim()));
        println(String.format("decoded2 message = %s", decoded2));
        */
    }
    
    public static void test02 () throws IOException {
        RSAKeys keys = RSA.generateKeys();
        
        OpenKey ok = keys.getOK();
        CloseKey ck = keys.getCK();
        
        String msg = "One Two Three Раз Два Три Проверка Связи";
        
        ByteArrayInputStream bais = new ByteArrayInputStream(msg.getBytes());
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        
        RSA.encode(bais, baos, ok);
        
        bais = new ByteArrayInputStream(baos.toByteArray());
        
        baos = new ByteArrayOutputStream();
        
        RSA.decode(bais, baos, ok, ck);
        
        String decoded = new String(baos.toByteArray());
        
        println(decoded);
    }
    
    public static void test03() throws IOException {
        RSAKeys keys = RSA.generateKeys();
        
        OpenKey ok = keys.getOK();
        CloseKey ck = keys.getCK();
        
        FileInputStream fis = new FileInputStream(new File("a.txt"));
        
        FileOutputStream fos = new FileOutputStream(new File("b.txt"));
        
        RSA.encode(fis, fos, ok);
        
        fis.close();
        fos.close();
        
        fis = new FileInputStream(new File("b.txt"));
        
        fos = new FileOutputStream(new File("c.txt"));
        
        RSA.decode(fis, fos, ok, ck);
        
        fis.close();
        fos.close();
    }
    
    public static void test04(String aFileName, String bFileName, String cFileName, String openFileName, String closeFileName) throws FileNotFoundException, IOException, ClassNotFoundException {
        RSAKeys keys = RSA.generateKeys();
        
        OpenKey ok = keys.getOK();
        CloseKey ck = keys.getCK();
        
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(openFileName)));
        oos.writeObject(ok);
        oos.close();
        
        oos = new ObjectOutputStream(new FileOutputStream(new File(closeFileName)));
        oos.writeObject(ck);
        oos.close();
        
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(openFileName)));
        ok = (OpenKey) ois.readObject();
        ois.close();
        
        ois = new ObjectInputStream(new FileInputStream(new File(closeFileName)));
        ck = (CloseKey) ois.readObject();
        ois.close();
        
        FileInputStream fis = new FileInputStream(new File(aFileName));
        
        FileOutputStream fos = new FileOutputStream(new File(bFileName));
        
        RSA.encode(fis, fos, ok);
        
        fis.close();
        fos.close();
        
        fis = new FileInputStream(new File(bFileName));
        
        fos = new FileOutputStream(new File(cFileName));
        
        RSA.decode(fis, fos, ok, ck);
        
        fis.close();
        fos.close();
    }
    
    public static void println(Object o) {
        System.out.println(o);
    }
}
